#include "read_iterator.h"

#include <cassert> // assert

namespace simple::io
{

	read_iterator::ptr_or_index::ptr_or_index(std::size_t index) noexcept
		: index(index)
	{}

	read_iterator::read_iterator() noexcept
		: handle(), buffer{}, current(nullptr), read_result(0)
	{}

	read_iterator::read_iterator(const unique_handle& handle, byte_range buffer) noexcept
		: handle(handle.get()), buffer(buffer), current(nullptr), read_result(0)
	{
		assert(buffer.begin() != nullptr);
		assert(buffer.end() != nullptr);
		assert(buffer.valid());
		read_next();
	}

	read_iterator::read_iterator(read_iterator&& other)
		: handle(other.handle), buffer(other.buffer), current(other.current), read_result(other.read_result)
	{
		other.current = nullptr;
		other.read_result.index = 0;
	}

	read_iterator& read_iterator::operator=(read_iterator&& other)
	{
		assert(handle == other.handle);
		assert(buffer == other.buffer);

		current = other.current;
		read_result = other.read_result;

		other.current = nullptr;
		other.read_result.index = 0;

		return *this;
	}

	void read_iterator::read_next() noexcept
	{
		auto result = read(handle, buffer);
		auto red = std::get_if<0>(&result);
		if(red && *red)
		{
			read_result.end = *red;
			current = buffer.begin();
		}
		else
		{
			// two possibilities here
			// red was nullptr, means we got a non zero index indicating the type of error
			// *red was nullpte, means we got an eof and index is 0
			read_result.index = result.index();
			current = nullptr;
		}
	}

	bool read_iterator::eof() const noexcept
	{
		return current == nullptr && read_result.index == 0;
	}

	int read_iterator::result_index() const noexcept
	{
		return current == nullptr ? read_result.index : 0;
	}

	read_iterator& read_iterator::operator++() noexcept
	{
		assert(current != nullptr);
		++current;
		if(current == read_result.end) read_next();
		return *this;
	}

	[[nodiscard]] read_iterator::reference read_iterator::operator*() noexcept
	{ assert(current != nullptr); return *current; }

	bool operator==(const read_iterator& one, const read_iterator& other) noexcept
	{
		// TODO: assert one or the other must be default constructed
		assert(one.result_index() == 0 || other.result_index() == 0);
		auto check = [](const auto& i, const auto& sentinel)
		{
			return sentinel.eof() &&
			(
				i.current == nullptr || // error or eof, either way stop iteration
				i.current == i.read_result.end // nothing more to read right now
			);
		};

		return check(one, other) || check(other, one);
	}
	bool operator!=(const read_iterator& one, const read_iterator& other) noexcept
	{ return !(one == other); }

} // namespace simple::io
